package com.spoplar.service.impl;

import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.TradeFundBill;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.trade.config.Configs;
import com.alipay.trade.model.ExtendParams;
import com.alipay.trade.model.builder.AlipayTradePayRequestBuilder;
import com.alipay.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.trade.model.builder.AlipayTradeQueryRequestBuilder;
import com.alipay.trade.model.result.AlipayF2FPayResult;
import com.alipay.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.trade.model.result.AlipayF2FQueryResult;
import com.alipay.trade.service.AlipayMonitorService;
import com.alipay.trade.service.AlipayTradeService;
import com.alipay.trade.service.impl.AlipayMonitorServiceImpl;
import com.alipay.trade.service.impl.AlipayTradeServiceImpl;
import com.alipay.trade.service.impl.AlipayTradeWithHBServiceImpl;
import com.alipay.trade.utils.Utils;
import com.alipay.trade.utils.ZxingUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spoplar.beans.AlipayTradePayResponseBody;
import com.spoplar.beans.AlipayTradePrecreateResponseBody;
import com.spoplar.common.TradeStatus;
import com.spoplar.pojo.MerchantAlipayConf;
import com.spoplar.pojo.MerchantAlipayTrade;
import com.spoplar.service.IMerchantAlipayTradeService;
import com.spoplar.service.IalipayService;
import com.spoplar.util.MathUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;

/**
 * Created by spoplar on 18-1-31.
 */
@Service("IAlipayService")
public class AlipayServiceImpl implements IalipayService {
    private static final Logger log = LoggerFactory.getLogger(AlipayServiceImpl.class);

    // 支付宝当面付2.0服务
    private static AlipayTradeService tradeService;

    // 支付宝当面付2.0服务（集成了交易保障接口逻辑）
    private static AlipayTradeService tradeWithHBService;

    // 支付宝交易保障接口服务，供测试接口api使用，请先阅读readme.txt
    private static AlipayMonitorService monitorService;

    static {
        Configs.init("zfbinfo.properties");
    }

    @Autowired
    private IMerchantAlipayTradeService iMerchantAlipayTradeService;


    //条码支付
    public AlipayTradePayResponse alipay_by_pay(MerchantAlipayConf merchantAlipayConf, MerchantAlipayTrade trade) {
        init(merchantAlipayConf);
        //组装数据(必选)
        String outTradeNo = trade.getOutTradeNo();
        String subject = trade.getSubject();
        String totalAmount = Double.toString(MathUtils.IntDivToDouble(trade.getTotalAmount(), 100, 2));
        String authCode = trade.getAuthCode();
        String storeId = trade.getStoreId();
        String body = trade.getBody();

        //可选
        String sellerId = trade.getSellerId();
        String operatorId = trade.getOperatorId();
        String timeoutExpress = trade.getTimeoutExpress();
        String providerId = merchantAlipayConf.getPid();
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId(providerId);

        //插入数据库
        trade.setTradeStatus(TradeStatus.PAYING.getCode());
        trade = defaultRequestSetting(merchantAlipayConf, trade, "bar_code");
        iMerchantAlipayTradeService.pre_pay(trade);

        // 创建条码支付请求builder，设置请求参数
        AlipayTradePayRequestBuilder builder = new AlipayTradePayRequestBuilder()
                //.setAppAuthToken(appAuthToken)
                .setOutTradeNo(outTradeNo)
                .setSubject(subject)
                .setAuthCode(authCode)
                .setTotalAmount(totalAmount)
                .setStoreId(storeId)
                .setBody(body)
                .setOperatorId(operatorId)
                .setExtendParams(extendParams)
                .setSellerId(sellerId)
                //.setGoodsDetailList(goodsDetailList)
                .setTimeoutExpress(timeoutExpress);


        // 调用tradePay方法获取当面付应答
        AlipayF2FPayResult result = tradeService.tradePay(builder);
        AlipayTradePayResponse resp = null;
        switch (result.getTradeStatus()) {
            case SUCCESS:
                AlipayTradePayResponse resp_tmp = result.getResponse();
                //下单返回插入数据库
                trade.setTradeStatus(TradeStatus.PAYING.getCode());
                trade = payingResponse(trade, resp_tmp, "bar_code");
                iMerchantAlipayTradeService.paying(trade);
                AlipayTradeQueryResponse queryResponse = this.AlipayTradeQuery(trade.getOutTradeNo());
                if (queryResponse != null) {
                    log.info("支付宝支付成功: )");
                    resp = resp_tmp;
                    trade.setTradeStatus(TradeStatus.PAYED.getCode());
                    iMerchantAlipayTradeService.paying(trade);
                    dumpResponse(resp);
                }
                break;

            case FAILED:
                log.error("支付宝支付失败!!!");
                break;

            case UNKNOWN:
                log.error("系统异常，订单状态未知!!!");
                break;

            default:
                log.error("不支持的交易状态，交易返回异常!!!");
                break;
        }
        return resp;

    }

    //扫码支付
    public String alipay_by_qrcode(MerchantAlipayConf merchantAlipayConf, MerchantAlipayTrade trade) {
        init(merchantAlipayConf);
        //组装数据(必选)
        String outTradeNo = trade.getOutTradeNo();
        String subject = trade.getSubject();
        String totalAmount = Double.toString(MathUtils.IntDivToDouble(trade.getTotalAmount(), 100, 2));
        String body = trade.getBody();
        String storeId = trade.getStoreId();
        //可选
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId(merchantAlipayConf.getPid());
        String timeoutExpress = trade.getTimeoutExpress();
        String sellerId = trade.getSellerId();
        String operatorId = trade.getOperatorId();

        //插入数据库
        trade.setTradeStatus(TradeStatus.PREPAYING.getCode());
        trade = defaultRequestSetting(merchantAlipayConf, trade, "qr_code");
        iMerchantAlipayTradeService.pre_pay(trade);

        // 创建扫码支付请求builder，设置请求参数
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
                .setSubject(subject)
                .setTotalAmount(totalAmount)
                .setOutTradeNo(outTradeNo)
                .setSellerId(sellerId)
                .setBody(body)
                .setOperatorId(operatorId)
                .setStoreId(storeId)
                .setExtendParams(extendParams)
                .setTimeoutExpress(timeoutExpress)
                .setNotifyUrl("http://www.spoplar.xyz");//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
        //.setGoodsDetailList(goodsDetailList);

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
        AlipayTradePrecreateResponse response = null;
        switch (result.getTradeStatus()) {
            case SUCCESS:
                log.info("支付宝预下单成功: )");

                response = result.getResponse();
                dumpResponse(response);
                //下单返回插入数据库

                ObjectMapper mapper = new ObjectMapper();
                JsonNode jsonNode = null;
                try {
                    jsonNode = mapper.readValue(response.getBody(), JsonNode.class);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                String sign = jsonNode.get("sign").asText();
                JsonNode responseNode = jsonNode.get("alipay_trade_precreate_response");
                String qr_code = responseNode.get("qr_code").asText();
                if (org.apache.commons.lang3.StringUtils.isNotEmpty(qr_code)) {
                    return qr_code;
                }
                // 需要修改为运行机器上的路径
                //String filePath = String.format("/home/spoplar/qrcode/qr-%s.png",response.getOutTradeNo());
                //log.info("filePath:" + filePath);
                //ZxingUtils.getQRCodeImge(response.getQrCode(), 256, filePath);
                break;
            case FAILED:
                log.error("支付宝预下单失败!!!");
                break;
            case UNKNOWN:
                log.error("系统异常，预下单状态未知!!!");
                break;
            default:
                log.error("不支持的交易状态，交易返回异常!!!");
                break;
        }
        return "";
    }

    //交易查询
    public AlipayTradeQueryResponse AlipayTradeQuery(String outTradeNo) {
        // 创建查询请求builder，设置请求参数
        AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder()
                .setOutTradeNo(outTradeNo);
        AlipayF2FQueryResult result = tradeService.queryTradeResult(builder);
        AlipayTradeQueryResponse response = null;
        switch (result.getTradeStatus()) {
            case SUCCESS:
                log.info("查询返回该订单支付成功: )");

                response = result.getResponse();
                dumpResponse(response);

                log.info(response.getTradeStatus());
                if (Utils.isListNotEmpty(response.getFundBillList())) {
                    for (TradeFundBill bill : response.getFundBillList()) {
                        log.info(bill.getFundChannel() + ":" + bill.getAmount());
                    }
                }
                break;

            case FAILED:
                log.error("查询返回该订单支付失败或被关闭!!!");
                break;

            case UNKNOWN:
                log.error("系统异常，订单支付状态未知!!!");
                break;

            default:
                log.error("不支持的交易状态，交易返回异常!!!");
                break;
        }
        return response;
    }

    //配置商户信息
    private synchronized void init(MerchantAlipayConf merchantAlipayConf) {
        if (merchantAlipayConf == null) {
            throw new IllegalStateException("merchantAlipayConf is null");
        }
        Configs.setPrivateKey(merchantAlipayConf.getPrivateKey());
        Configs.setPublicKey(merchantAlipayConf.getPublicKey());
        Configs.setAlipayPublicKey(merchantAlipayConf.getAlipayPublicKey());
        Configs.setAppid(merchantAlipayConf.getAppid());
        Configs.setPid(merchantAlipayConf.getPid());
        Configs.setSignType(merchantAlipayConf.getSignType());

        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

        // 支付宝当面付2.0服务（集成了交易保障接口逻辑）
        tradeWithHBService = new AlipayTradeWithHBServiceImpl.ClientBuilder().build();

        monitorService = new AlipayMonitorServiceImpl.ClientBuilder()
                .setGatewayUrl("http://mcloudmonitor.com/gateway.do").setCharset("UTF-8")
                .setFormat("json").build();
    }


    private MerchantAlipayTrade defaultRequestSetting(MerchantAlipayConf merchantAlipayConf, MerchantAlipayTrade trade, String scene) {
        //默认设置
        switch (scene) {
            case "qr_code":
                trade.setMethod("alipay.trade.precreate");
                trade.setScene("qr_code");
                break;
            case "bar_code":
                trade.setMethod("alipay.trade.pay");
                trade.setScene("bar_code");
                break;
            default:
                trade.setMethod("alipay.trade.precreate");
                trade.setScene("qr_code");
                break;
        }
        trade.setFormat("JSON");
        trade.setCharset("UTF-8");
        trade.setSignType(merchantAlipayConf.getSignType());
        trade.setTimestamp(new Date());
        ObjectMapper mapper = new ObjectMapper();
        try {
            trade.setRequestContent(mapper.writeValueAsString(trade));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return trade;
    }

    private MerchantAlipayTrade payingResponse(MerchantAlipayTrade trade, AlipayResponse response, String payType) {

        ObjectMapper mapper = new ObjectMapper();
        String sign = org.apache.commons.lang3.StringUtils.EMPTY;
        JsonNode node = null;
        try {
            node = mapper.readValue(response.getBody(), JsonNode.class);
            sign = node.get("sign").asText();
        } catch (IOException e) {
            e.printStackTrace();
        }
        JsonNode responseNode = null;
        switch (payType) {
            case "bar_code":
                responseNode = node.get("alipay_trade_pay_response");
                trade.setCode(responseNode.get("code").asText());
                trade.setMsg(responseNode.get("msg").asText());
                trade.setBuyerUserId(responseNode.get("buyer_user_id").asText());
                trade.setBuyerUserType(responseNode.get("buyer_user_type").asText());
                break;
            case "qr_code":
                /**
                 *array (
                 'gmt_create' => '2018-02-02 13:32:47',
                 'charset' => 'utf-8',
                 'seller_email' => 'hkvkps3782@sandbox.com',
                 'subject' => 'bbbb',
                 'sign' => 'RC/kEX18+CHdvVJJtRyDMQG06o7AyJc7CcBxapz1nCub/wldh35WvQEPie5hRjYKp/zjj5EA5eN9U0sxV4FKRJhOe2HrYq0G7CtM3JxI/du3z8TGSjJ0yytrlXraJWbCPlVYA5EFa62+UPN8If8UeITcuQIax8+v3jzQS5ZPF28=',
                 'buyer_id' => '2088102172375444',
                 'invoice_amount' => '0.10',
                 'notify_id' => '83122d6df2be362d3b3135837e4b5d2jea',
                 'fund_bill_list' => '[{"amount":"0.10","fundChannel":"ALIPAYACCOUNT"}]',
                 'notify_type' => 'trade_status_sync',
                 'trade_status' => 'TRADE_SUCCESS',
                 'receipt_amount' => '0.10',
                 'app_id' => '2016080600177176',
                 'buyer_pay_amount' => '0.10',
                 'sign_type' => 'RSA',
                 'seller_id' => '2088102170140783',
                 'gmt_payment' => '2018-02-02 13:32:55',
                 'notify_time' => '2018-02-02 13:32:56',
                 'version' => '1.0',
                 'out_trade_no' => 'ffff666fa889944458',
                 'total_amount' => '0.10',
                 'trade_no' => '2018020221001004440200235359',
                 'auth_app_id' => '2016080600177176',
                 'buyer_logon_id' => 'iwn***@sandbox.com',
                 'point_amount' => '0.00',
                 )

                 */
                responseNode = node;
                trade.setBuyerId(responseNode.get("buyer_id").asText());
                break;
        }
        trade.setSubCode(response.getSubCode());
        trade.setSubMsg(response.getSubMsg());
        trade.setTradeNo(responseNode.get("trade_no").asText());
        trade.setBuyerLogonId(responseNode.get("buyer_logon_id").asText());
        trade.setBuyerPayAmount((int) MathUtils.StringMulToDouble(responseNode.get("buyer_pay_amount").asText(), "100", 2));
        trade.setReceiptAmount((int) MathUtils.StringMulToDouble(responseNode.get("receipt_amount").asText(), "100", 2));
        trade.setPointAmount((int) MathUtils.StringMulToDouble(responseNode.get("point_amount").asText(), "100", 2));
        trade.setInvoiceAmount((int) MathUtils.StringMulToDouble(responseNode.get("invoice_amount").asText(), "100", 2));
        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
        DateTime dateTime = DateTime.parse(responseNode.get("gmt_payment").asText(), formatter);
        trade.setGmtPayment(dateTime.toDate());
        trade.setFundBillList(responseNode.toString());
        trade.setSign(sign);
        return trade;
    }


    private void dumpResponse(AlipayResponse response) {
        if (response != null) {
            log.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
            if (StringUtils.isNotEmpty(response.getSubCode())) {
                log.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(), response.getSubMsg()));
            }
            log.info("body:" + response.getBody());
        }
    }


}
